package modelado.algoritmos;

import java.util.SortedSet;

import modelado.datos.elementos.Elemento;
import modelado.estados.Estado;
import modelado.problemas.ProblemaRyP;
import modelado.soluciones.Soluciones;
import modelado.soluciones.SolucionesImpl;
import modelado.tipos.TipoDeObjetivo;
import utiles.Metricas;
import utiles.MetricasImpl;

public class AlgoritmoRamificaYPodaImpl implements Algoritmo {

	private ProblemaRyP problema;
	private Estado estado;
	private TipoDeObjetivo tipo;
	Soluciones soluciones;
	private Metricas metricas;

	public AlgoritmoRamificaYPodaImpl(ProblemaRyP p) {
		problema = p;

		tipo = problema.getTipoDeObjetivo();
		soluciones = new SolucionesImpl(p);
		metricas = new MetricasImpl();
	}

	public void ejecuta() {

		metricas.setTiempoDeEjecucionInicial();

		estado = problema.getEstadoInicial();

		estado.setSoluciones(soluciones);
		estado.setMetricas(metricas);

		bt();

		metricas.setTiempoDeEjecucionFinal();
	}

	private boolean unaSolucion() {
		return (tipo.equals(TipoDeObjetivo.UNA_SOLUCION) || tipo
				.equals(TipoDeObjetivo.HAY_SOLUCION))
				&& soluciones.haySolucion();
	}

	private boolean bt() {
		boolean exito = false;
		soluciones.addSolucion(estado);
		if (unaSolucion()) {
			exito = true;
		} else {
			SortedSet<Elemento> candidatos = problema.getCandidatos(estado);
			for (Elemento e : candidatos) {
				for (Elemento e1 : e) {
					
					if (problema.cumpleRestricciones(e1, estado)) {
						
						estado.addElemento(e1);
						metricas.addIteracion();
						exito = bt();
						estado.remove();
						if (exito)
							break;
					}
				}
				if (exito)
					break;
			}
		}
		return exito;
	}

	public Metricas getMetricas() {
		return metricas;
	}

	public Soluciones getSoluciones() {
		return soluciones;
	}

}
